package com.cmpe.group6.storyroad;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.cmpe.group6.storyroad.ListAdapters.CommentAdapter;
import com.cmpe.group6.storyroad.ListAdapters.StoryAdapter;
import com.cmpe.group6.storyroad.jsonClasses.Comment;
import com.cmpe.group6.storyroad.jsonClasses.ServerResponseObject;
import com.cmpe.group6.storyroad.jsonClasses.StoryLinkObject;
import com.cmpe.group6.storyroad.jsonClasses.StoryObject;
import com.cmpe.group6.storyroad.jsonClasses.StoryObject.Coordinates;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.gson.Gson;

/** Activity for reading a story in the system.
 *
 */
public class ReadStoryActivity extends Activity {

	private StoryObject storyObject;
	private ArrayList<StoryLinkObject> linkedStories;
	private ReadStoryTask rTask;
	private Activity currentActivity;
	private CommentAdapter commentAdapter;
	private StoryAdapter storyAdapter;

	private View storyContainer;
	private TextView storyTitle;
	private TextView username;
	private TextView rating;
	private TextView tags;
	private EditText commentBody;
	private Button addFragment;
	private Button addComment;
	private Spinner rateit;
	private ListView commentList;
	private ExpandableListView storyList;
	private ProgressBar progressBar;
	private GoogleMap googleMap;

	private final ArrayList<String> usernames = new ArrayList<String>();
	private final ArrayList<String> bodies = new ArrayList<String>();
	private final ArrayList<String> dates = new ArrayList<String>();

	private String storyId;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_read_story);
		final ActionBar bar = getActionBar();
		bar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
		bar.setDisplayShowTitleEnabled(false);

		currentActivity = this;

		storyContainer = findViewById(R.id.story_container);
		storyTitle = (TextView) findViewById(R.id.story_title);
		addFragment = (Button) findViewById(R.id.link_fragment);
		username = (TextView) findViewById(R.id.user_title);
		rating = (TextView) findViewById(R.id.rating_current);
		tags = (TextView) findViewById(R.id.tags);
		rateit = (Spinner) findViewById(R.id.rating_menu);
		addComment = (Button) findViewById(R.id.add_comment_button);
		commentBody = (EditText) findViewById(R.id.comment_body);
		commentList = (ListView) findViewById(R.id.comments_list);
		storyList = (ExpandableListView) findViewById(R.id.storyList);
		progressBar = (ProgressBar) findViewById(R.id.progressBar_story);
		googleMap = ((MapFragment) getFragmentManager().findFragmentById(
				R.id.map)).getMap();
		storyId = getIntent().getStringExtra("story_id");

		storyContainer.setVisibility(View.INVISIBLE);

		rTask = new ReadStoryTask();
		rTask.execute((Void) null);

		ImageView transparentImageView = (ImageView) findViewById(R.id.transparent_image);
		final ScrollView mainScrollView = (ScrollView) findViewById(R.id.main_scroll);

		// Making map usage smoother.
		transparentImageView.setOnTouchListener(new View.OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				int action = event.getAction();
				switch (action) {
				case MotionEvent.ACTION_DOWN:
					// Disallow ScrollView to intercept touch events.
					mainScrollView.requestDisallowInterceptTouchEvent(true);
					// Disable touch on transparent view
					return false;

				case MotionEvent.ACTION_UP:
					// Allow ScrollView to intercept touch events.
					mainScrollView.requestDisallowInterceptTouchEvent(false);
					return true;

				case MotionEvent.ACTION_MOVE:
					mainScrollView.requestDisallowInterceptTouchEvent(true);
					return false;

				default:
					return true;
				}
			}
		});

		// Clicking the username will direct the user to that user's profile
		// page.
		username.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Intent profileIntent = new Intent(currentActivity,
						ProfileActivity.class);
				profileIntent.putExtra("targetname", storyObject.getUserName());
				startActivity(profileIntent);
			}
		});

		// Add comment functionality.
		addComment.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				new AddCommentTask().execute((Void) null);
			}
		});

	}

	/*
	 * Because android do not want to put listview into a scrollview, this
	 * function dynamically sets the height of the list so the whole list is
	 * visible.
	 */
	private void setListViewHeight(Adapter adapter, ListView list) {

		if (adapter == null) {
			// pre-condition
			return;
		}

		int totalHeight = 0;
		for (int i = 0; i < adapter.getCount(); i++) {

			View listItem = adapter.getView(i, null, list);
			if (listItem instanceof ViewGroup) {
				listItem.setLayoutParams(new LayoutParams(
						LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
			}
			listItem.measure(0, 0);
			totalHeight += listItem.getMeasuredHeight();
		}

		ViewGroup.LayoutParams params = list.getLayoutParams();
		params.height = totalHeight
				+ (list.getDividerHeight() * (adapter.getCount() - 1));
		list.setLayoutParams(params);
	}

	/*
	 * Because android do not want to put listview into a scrollview, this
	 * function dynamically sets the height of the list so the whole list is
	 * visible.
	 */
	private void setListViewHeight(ExpandableListView list) {

		ExpandableListAdapter adapter = list.getExpandableListAdapter();

		if (adapter == null) {
			// pre-condition
			return;
		}

		int totalHeight = 0;
		for (int i = 0; i < adapter.getGroupCount(); i++) {

			View listItem = adapter.getGroupView(i, true, null, list);
			if (listItem instanceof ViewGroup) {
				listItem.setLayoutParams(new LayoutParams(
						LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
			}
			listItem.measure(0, 0);
			totalHeight += listItem.getMeasuredHeight();
			for (int j = 0; j < adapter.getChildrenCount(i); j++) {
				View childItem = adapter.getChildView(i, j, false, null, list);


				childItem.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED);
				totalHeight += childItem.getMeasuredHeight()+100;
			}
		}
		
		ViewGroup.LayoutParams params = list.getLayoutParams();
		params.height = totalHeight
				+ (list.getDividerHeight() * (adapter.getGroupCount() - 1));
		list.setLayoutParams(params);
	}

	/*
	 * Getting the child levels of each story related to the current story.
	 * 
	 * @param stories stories linked to this story.
	 * 
	 * @return Map containing the levels mapped to story ids.
	 */
	private HashMap<Integer, Integer> getLevels(
			ArrayList<StoryLinkObject> stories) {

		HashMap<Integer, Integer> countMap = new HashMap<Integer, Integer>();

		countMap.put(stories.get(0).getStory_id(), 0);
		for (int i = 1; i < stories.size(); i++) {
			int currentParent = stories.get(i).getParent_id();
			int currentId = stories.get(i).getStory_id();
			countMap.put(currentId, countMap.get(currentParent) + 1);
		}

		return countMap;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	// Activating the top bar.
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.home_button:
			Intent mainIntent = new Intent(this, MainActivity.class);
			startActivity(mainIntent);
			return true;
		case R.id.recommendations_button:
			Intent recomIntent = new Intent(this, RecommendationsActivity.class);
			startActivity(recomIntent);
			return true;
		case R.id.profile_button:
			Intent profileIntent = new Intent(this, ProfileActivity.class);
			startActivity(profileIntent);
			return true;
		case R.id.search_button:
			Intent searchIntent = new Intent(this, SearchActivity.class);
			startActivity(searchIntent);
			return true;
		case R.id.following_button:
			Intent followIntent = new Intent(this, FollowingActivity.class);
			startActivity(followIntent);
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	/**
	 * Represents an asynchronous Read Story task to getting required
	 * information of the chosen story.
	 * 
	 */
	public class ReadStoryTask extends AsyncTask<Void, Void, Boolean> {

		@Override
		protected void onPreExecute() {
			progressBar.setVisibility(View.VISIBLE);
		}

		@Override
		protected Boolean doInBackground(Void... params) {
			DefaultHttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost(Utils.SERVER_NAME + "ReadStory");

			try {
				// Add your data
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
						3);
				nameValuePairs.add(new BasicNameValuePair("request_type",
						"Android"));
				nameValuePairs.add(new BasicNameValuePair("story_id", storyId));
				nameValuePairs.add(new BasicNameValuePair("username",
						Utils.username));
				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

				// Execute HTTP Post Request
				HttpResponse response = httpclient.execute(httppost);

				String jsonResponse = EntityUtils
						.toString(response.getEntity());
				Gson gson = new Gson();
				storyObject = gson.fromJson(jsonResponse, StoryObject.class);

			} catch (ClientProtocolException e) {
				// TODO Auto-generated catch block
			} catch (IOException e) {
				// TODO Auto-generated catch block
			}

			return true;
		}

		@Override
		protected void onPostExecute(final Boolean success) {

			if (!success) {
				Toast.makeText(getApplicationContext(), "Connection Error",
						Toast.LENGTH_SHORT).show();
			} else {
				storyTitle.setText(storyObject.getTitle());
				username.setText(storyObject.getUserName());
				DecimalFormat numberFormat = new DecimalFormat("#.#");
				rating.setText("Rating: "
						+ String.valueOf(numberFormat.format((storyObject
								.getTotalRating() * 1.0)
								/ storyObject.getRatingCount())));
				tags.setText(storyObject.getTags());

				ArrayList<Comment> comments = storyObject.getComments();

				for (int i = 0; i < comments.size(); i++) {
					usernames.add(comments.get(i).getUsername());
					bodies.add(comments.get(i).getBody());
					dates.add(comments.get(i).getDate());
				}

				final HashMap<String, String> storyMap = new HashMap<String, String>();
				final List<String> titles = new ArrayList<String>();
				final List<Integer> ids = new ArrayList<Integer>();
				linkedStories = storyObject.getStoryLinks();
				HashMap<Integer, Integer> idCountMap = getLevels(linkedStories);
				int targetIndex = -1;
				int counter = 0;
				Map<Integer, Coordinates> coordinateMap = storyObject
						.getStoryCoordinateMap();
				/*
				 * Creating linked story ids, titles, coordinates and bodies.
				 */
				for (StoryLinkObject linkObject : linkedStories) {

					ids.add(linkObject.getStory_id());

					String title = "";
					for (int i = 0; i < idCountMap
							.get(linkObject.getStory_id()); i++) {
						title += " ";
					}
					for (int i = 0; i < idCountMap
							.get(linkObject.getStory_id()); i++) {
						title += ">";
					}

					title += storyObject.getStoryTitleMap().get(
							linkObject.getStory_id());
					title += " ("
							+ storyObject.getStoryUserMap().get(
									linkObject.getStory_id()) + ")";

					storyMap.put(
							title,
							storyObject.getStoryBodyMap().get(
									linkObject.getStory_id()));
					titles.add(title);
					if (storyObject.getS_id() == linkObject.getStory_id()) {
						targetIndex = counter;
					}
					++counter;

					// Adding marker for each linked story to the map.
					Marker marker = googleMap
							.addMarker(new MarkerOptions()
									.position(
											new LatLng(coordinateMap.get(
													linkObject.getStory_id())
													.getX(), coordinateMap.get(
													linkObject.getStory_id())
													.getY()))
									.draggable(false)
									.icon(BitmapDescriptorFactory
											.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))
									.visible(true));

					if (linkObject.getStory_id() == storyObject.getS_id()) {
						LatLng coordinates = new LatLng(
								storyObject.getX_coord(),
								storyObject.getY_coord());
						marker.setIcon(BitmapDescriptorFactory
								.defaultMarker(BitmapDescriptorFactory.HUE_RED));
						CameraUpdate cameraUpdate = CameraUpdateFactory
								.newLatLngZoom(coordinates, 11);
						googleMap.animateCamera(cameraUpdate);
					}
				}

				storyAdapter = new StoryAdapter(currentActivity, titles,
						storyMap, ids);
				storyList.setAdapter(storyAdapter);
				
				for(int i=0;i<storyAdapter.getGroupCount();i++){
					storyList.expandGroup(i);
				}

				setListViewHeight(storyList);
				
				for(int i=0;i<storyAdapter.getGroupCount();i++){
					storyList.collapseGroup(i);
				}

				storyList.expandGroup(targetIndex,true);

				commentAdapter = new CommentAdapter(currentActivity, usernames,
						bodies, dates);
				commentList.setAdapter(commentAdapter);

				setListViewHeight(commentAdapter, commentList);

				if (storyObject.getAllowedList().equals("public")
						|| storyObject.getAllowedList()
								.contains(Utils.username)) {

					addFragment.setVisibility(View.VISIBLE);
					addFragment.setOnClickListener(new OnClickListener() {

						@Override
						public void onClick(View v) {
							Intent linkFragmentIntent = new Intent(
									currentActivity, CreateStory.class);
							linkFragmentIntent.putExtra("story_id", storyId);
							startActivity(linkFragmentIntent);
						}
					});
				}

				// Rating operation.
				if (storyObject.getMyRating() < 1) {
					rateit.setOnItemSelectedListener(new OnItemSelectedListener() {

						@Override
						public void onItemSelected(AdapterView<?> arg0,
								View arg1, int arg2, long arg3) {
							if (!rateit.getSelectedItem().toString()
									.equals("0")) {
								new RatingTask().execute(rateit
										.getSelectedItem().toString());
							}
						}

						@Override
						public void onNothingSelected(AdapterView<?> arg0) {
							// TODO Auto-generated method stub

						}
					});
				} else {
					rateit.setSelection(storyObject.getMyRating());
					rateit.setClickable(false);
				}

				// Long clicking a linked story will direct the user to that
				// story's page.
				storyList
						.setOnItemLongClickListener(new OnItemLongClickListener() {

							@Override
							public boolean onItemLongClick(
									AdapterView<?> parent, View view,
									int position, long id) {

								int itemType = ExpandableListView
										.getPackedPositionType(id);

								if (itemType == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
									long packedPos = ((ExpandableListView) parent)
											.getExpandableListPosition(position);

									int groupPosition = ExpandableListView
											.getPackedPositionGroup(packedPos);

									int targetStoryId = (Integer) storyList
											.getExpandableListAdapter()
											.getGroup(groupPosition);
									Intent newStoryIntent = new Intent(
											currentActivity,
											ReadStoryActivity.class);
									newStoryIntent.putExtra("story_id",
											String.valueOf(targetStoryId));
									startActivity(newStoryIntent);

								}
								return false;
							}
						});
			}

			progressBar.setVisibility(View.GONE);
			storyContainer.setVisibility(View.VISIBLE);
		}

		@Override
		protected void onCancelled() {

		}
	}

	/**
	 * Represents an asynchronous Rating task to send the rating given by the
	 * user for this story to server.
	 * 
	 */
	public class RatingTask extends AsyncTask<String, Void, Boolean> {
		@Override
		protected Boolean doInBackground(String... params) {
			String rating = params[0];
			DefaultHttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost(Utils.SERVER_NAME + "RateStory");
			ServerResponseObject responseObject = new ServerResponseObject();
			try {
				// Add your data
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
						3);
				nameValuePairs.add(new BasicNameValuePair("request_type",
						"Android"));
				nameValuePairs.add(new BasicNameValuePair("story_id", storyId));
				nameValuePairs.add(new BasicNameValuePair("username",
						Utils.username));
				nameValuePairs.add(new BasicNameValuePair("rating", rating));
				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

				// Execute HTTP Post Request
				HttpResponse response = httpclient.execute(httppost);

				String jsonResponse = EntityUtils
						.toString(response.getEntity());
				Gson gson = new Gson();
				responseObject = gson.fromJson(jsonResponse,
						ServerResponseObject.class);

			} catch (ClientProtocolException e) {
				// TODO Auto-generated catch block
			} catch (IOException e) {
				// TODO Auto-generated catch block
			}
			if (responseObject != null && responseObject.isRateSuccess()) {
				return true;
			} else {
				return false;
			}
		}

		@Override
		protected void onPostExecute(final Boolean success) {

			if (!success) {
				Toast.makeText(getApplicationContext(),
						"You already rated for this story!", Toast.LENGTH_SHORT)
						.show();
			}

		}

		@Override
		protected void onCancelled() {

		}
	}

	/**
	 * Represents an Add Comment task to send the comment entered by the user
	 * for this story to the server.
	 * 
	 */
	public class AddCommentTask extends AsyncTask<Void, Void, Boolean> {
		@Override
		protected Boolean doInBackground(Void... params) {
			DefaultHttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost(Utils.SERVER_NAME + "AddComment");
			ServerResponseObject responseObject = new ServerResponseObject();
			String comment = commentBody.getText().toString();
			if (comment.length() > 140 || comment.length() == 0) {
				this.cancel(true);
			}
			try {
				// Add your data
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
				nameValuePairs.add(new BasicNameValuePair("request_type",
						"Android"));
				nameValuePairs.add(new BasicNameValuePair("story_id", storyId));
				nameValuePairs.add(new BasicNameValuePair("username",
						Utils.username));
				nameValuePairs.add(new BasicNameValuePair("comment_body",
						comment));
				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

				// Execute HTTP Post Request
				HttpResponse response = httpclient.execute(httppost);

				String jsonResponse = EntityUtils
						.toString(response.getEntity());
				Gson gson = new Gson();
				responseObject = gson.fromJson(jsonResponse,
						ServerResponseObject.class);

			} catch (ClientProtocolException e) {
				// TODO Auto-generated catch block
			} catch (IOException e) {
				// TODO Auto-generated catch block
			}

			if (responseObject.isAddCommentSuccess()) {
				return true;
			} else {
				return false;
			}
		}

		@Override
		protected void onPostExecute(final Boolean success) {

			if (!success) {
				Toast.makeText(getApplicationContext(), "Connection Error",
						Toast.LENGTH_SHORT).show();
			} else {
				Intent restartIntent = new Intent(currentActivity,
						ReadStoryActivity.class);
				restartIntent.putExtra("story_id", storyId);
				startActivity(restartIntent);
			}
		}

		@Override
		protected void onCancelled() {
			Toast.makeText(getApplicationContext(),
					"Your comment should be between 0 and 140 characters.",
					Toast.LENGTH_SHORT).show();
		}
	}

}
